/**
 * @FileName: RechargeOrderService.java
 * @Author
 * @Description:
 * @Date 2016年5月11日 下午8:37:53
 * @CopyRight ZTE Corporation
 */
package cn.hpclub.server.service;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.google.common.net.MediaType;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;

import cn.hpclub.server.constant.Constant;
import cn.hpclub.server.exception.CheckParamException;
import cn.hpclub.server.model.MemberOrg;
import cn.hpclub.server.model.Organization;
import cn.hpclub.server.model.RechargeOrder;
import cn.hpclub.server.model.SalePromotion;
import cn.hpclub.server.util.DateKit;
import cn.hpclub.server.util.HttpUtil;
import cn.hpclub.server.util.InterfaceUtil;
import cn.hpclub.server.util.SystemConfigUtil;
import cn.hpclub.server.util.Utils;

public class RechargeOrderService{
    private static final Logger              logger                                  = LoggerFactory
            .getLogger(RechargeOrderService.class);
    public static final RechargeOrderService service                                 = new RechargeOrderService();
    private String                           REST_HOST_PREFIX                        = SystemConfigUtil
            .getSystemConfig().getRestHostPrefix();

    private static final String              DETAIL_MSG                              = "DETAIL_MSG";
    private static final String              PAY_ACCEPT                              = "PAY_ACCEPT";
    private static final String              OUT_DATA                                = "OUT_DATA";
    private static final String              RETURN_CODE                             = "RETURN_CODE";
    private static final String              ROOT                                    = "ROOT";

    private static final int                 RECHARGE_ORDER_OPERATED_DESC_MAX_LENGTH = 512;

    /**
     * @Title: getRechargeOrderByOrderId
     * @Description:TODO
     * @paramstr
     * @return Record
     */

    public Record getRechargeOrderByOrderId(String orderId){
        String sql = "select cc.pos_id,cc.pub,ro.* from recharge_order ro left join member_org mo on mo.id=ro.user_id left join organization o on o.id=mo.org_id left join cmcc_city city on city.group_id=(select org.NumCode from organization org where org.id=o.CityCode) left join city_counter cc on cc.cmcc_city_id=city.id where ro.order_id=?";

        return Db.findFirst(sql, orderId);
    }

    public String getSqlSelect(){
        // return " select o.*,DATE_FORMAT(o.pay_time,'%Y-%m-%d %T') as
        // pay_datetime ,DATE_FORMAT(o.create_time,'%Y-%m-%d %T') as
        // created_datetime,o.user_phone as staff_phone,o.user_name as
        // staff_name,m.emp_code,(case o.pay_plat when 14 then '支付宝' when 15
        // then '建行' when 16 then '银联' else '出错' end)as pay_method,(case
        // o.pay_status when 0 then '未支付' when 1 then '已支付' when 2 then '支付失败'
        // when 5 then '已退款' when 6 then '退款失败' else '出错' end)as pay_state,(case
        // o.order_status when 5 then '充值成功' when 6 then '失败' else '未知' end)as
        // order_state,sp.order_id as cmcc_id ";
        String sql = " select o.*," + InterfaceUtil.getCitySql("o.user_org_id") + " as city, "
                + InterfaceUtil.getAreaSql("o.user_org_id") + " as area, "
                + " org.NumCode, org.DisplayName,  (select class_name from organization_class where enabled = '1' and id = org.webSiteId) as sitetype,  "
                + "DATE_FORMAT(o.pay_time,'%Y-%m-%d %T') as pay_datetime ,DATE_FORMAT(o.create_time,'%Y-%m-%d %T') as created_datetime,o.user_phone as staff_phone,"
                + "o.user_name as staff_name,m.emp_code,(case o.pay_plat when 14 then '支付宝' when 15 then '建行' when 16 then '银联'  when 20 then '微信' else '出错' end)as pay_method,"
                + "(case o.pay_status when 0 then '未支付' when 1 then '已支付' when 2 then '支付失败' when 5 then '已退款' when 6 then '退款失败' else '出错' end)as pay_state,"
                + "(case o.order_status when 5 then '充值成功' when 6 then '充值失败' when 7 then '网络异常' when 0 then '未知' when 8 then '解析失败' when 1 then '未发起' end)as order_state,"
                + "sp.order_id as cmcc_id,o.desc_details as refund_desc,DATE_FORMAT(o.refund_time,'%Y-%m-%d %T') as refund_datetime,"
                + "o.recharge_desc,DATE_FORMAT(o.recharge_time,'%Y-%m-%d %T') as recharge_datetime,"
                + "IF((o.pay_plat=15||o.pay_plat=16||o.pay_plat=20)&&(o.pay_status=1||o.pay_status=6)&&(o.order_status!=5)&&(o.pay_amount>0)&&"
                +
                // "(DATE_FORMAT(o.create_time,'%Y-%m-%d')=DATE_FORMAT(CURDATE(),'%Y-%m-%d')),'1','0')
                // as can_refund";
                " o.create_time < date_sub(now(),interval 10 minute),'1','0') as can_refund";
        return sql;
    }

    public String getSqlFilter(ConcurrentHashMap<String, Object> searchInfo){
        // String sqlExceptSelect = " from recharge_order o left join member_org
        // m on o.user_id=m.id left join sales_promotion sp on
        // o.order_id=sp.thirdpart_id where 1=1 ";
        String sqlExceptSelect = " from recharge_order o left join member_org m on o.user_id=m.id left join organization org on o.user_org_id=org.id left join sales_promotion sp on o.order_id=sp.thirdpart_id where 1=1 ";

        for(Entry<String, Object> item : searchInfo.entrySet()){
            sqlExceptSelect += appendQueryFilter(item);
        }
        // sqlExceptSelect += getOrgLimit(InterfaceUtil.getOrgId() + "");// 数据限制
        sqlExceptSelect += " ORDER BY o.create_time DESC";

        return sqlExceptSelect;
    }

    /**
     * @Title: buildQuery
     * @Description:追加查询条件
     */
    private String appendQueryFilter(Entry<String, Object> item){
        StringBuilder sb = new StringBuilder();
        Object valueObj = item.getValue();
        String value = "";
        String key = item.getKey();
        if(null != valueObj){
            value = String.valueOf(valueObj);
        }
        switch(key){
            // case "org_id":
            // sb.append(getSubOrgIdFilter(value));
            // break;
            case "order_id":
                sb.append(getAndLikeFilter("o.order_id", value));
                break;
            case "phone_number":
                sb.append(getAndLikeFilter("o.recharge_phone", value));
                break;
            case "cmcc_id":
                sb.append(getAndLikeFilter("sp.order_id", value));
                // System.out.println("subsql:" +
                // getAndLikeFilter("sp.thirdpart_id", value));
                break;
            case "staff_phone":
               // sb.append(getAndLikeFilter("m.name", value));
                sb.append(getAndLikeFilter("m.mobile", value));
                break;
            case "pay_plat":
                if(!value.equals("-1")){
                    sb.append(getPayPlatFilter(value));
                }
                break;
            case "pay_status":
                JSONArray valueJA = JSONArray.parseArray(value);
                sb.append(getPayStatusFilter("o.pay_status", valueJA));
                break;
            case "order_status":
                if(!value.equals("-1")){
                    sb.append(getOrderStatusFilter(value));
                }
                break;

            case "start_date":
                sb.append(getStartTimeFilter(value));
                break;
            case "end_date":
                sb.append(getEndTimeFilter(value));
                break;
            case "org_id":
                sb.append(getOrgLimit(value));
                break;
            // case "creator":
            // sb.append(getCreatorFilter(value));
            // break;
            default:
                break;
        }
        return sb.toString();
    }

    /**
     * @Title: getPayStatusFilter
     * @Description:TODO
     * @paramstring
     * @param valueJA
     * @return Object
     */
    private Object getPayStatusFilter(String keyName, JSONArray valueJA){
        String sql = "";
        boolean isAll = false;
        for(int i = 0, len = valueJA.size(); i < len; i++){
            String va = (String)valueJA.get(i);
            if(i == 0){
                if(!"-1".equals(va)){
                    sql += " AND (" + keyName + " = " + va;
                } else{
                    isAll = true;
                }
            } else{
                if(!"-1".equals(va)){
                    if(isAll){
                        sql += " AND (" + keyName + " = " + va;
                        isAll = false;
                    } else{
                        sql += " OR " + keyName + " = " + va;
                    }
                }
            }
            if((i == len - 1) && !isAll){
                sql += ")";
            }

        }

        return StrKit.notBlank(sql) ? String.format(sql) : "";
    }

    /**
     * @Title: getOrgLimit
     * @Description:TODO
     * @paramstring
     * @return Object
     */
    private Object getOrgLimit(String orgId){
        String sql = " and o.user_org_id in("
                + InterfaceUtil.getChlidOrgIdSql(Integer.parseInt(orgId), Constant.MAX_ORG_LEVEL, true) + ") ";
        return String.format(sql);
    }

    private String getPayPlatFilter(String value){
        if(value.equals("1")){
            return String.format(" AND o.pay_plat = 14 ");
        }
        if(value.equals("3")){
            return String.format(" AND o.pay_plat = 20 ");
        } else{
            return String.format(" AND ((o.pay_plat = 15) OR (o.pay_plat = 16)) ");
        }

    }

    private String getOrderStatusFilter(String value){
        if(value.equals("1")){
            return String.format(" AND o.order_status = 5 ");
        } else{
            return String
                    .format(" AND ((o.order_status = 0 ) OR (o.order_status = 1) OR (o.order_status = 6) OR (o.order_status = 7) OR (o.order_status = 8)) ");
        }

    }

    /**
     * @Title: getEndTimeFilter
     * @Description:获取开始时间查询条件
     */
    private String getEndTimeFilter(String value){
        return String.format(" AND o.create_time <= '%s 23:59:59' ", value);
    }

    /**
     * @Title: getStartTimeFilter
     * @Description:获取结束时间查询条件
     */
    private String getStartTimeFilter(String value){
        return String.format(" AND o.create_time >= '%s 00:00:00' ", value);
    }

    /**
     * @Title: getAndLikeFilter
     * @Description:获取KeyName和value模糊查询条件
     */
    private String getAndLikeFilter(String keyName, String value){
        return String.format(" AND %s LIKE '%%%s%%' ", keyName, value);
    }

    /**
     * @Title: updateRechargeOrderStatus
     * @Description:更新订单的办理状态，捕获并忽略异常
     * @param orderId
     * @param bossNo
     * @param statusNo
     * @param statusText
     * @param rechargeDesc
     * @return boolean
     */
    public boolean updateRechargeOrderStatus(String orderId, String bossNo, int statusNo, String statusText,
            String rechargeDesc){
        try{
            return new RechargeOrder().updateOrderStatus(orderId, bossNo, statusNo,
                                                         Utils.getShortMsg(statusText,
                                                                           RECHARGE_ORDER_OPERATED_DESC_MAX_LENGTH),
                                                         rechargeDesc);

        }
        catch(Exception e){
            // e.printStackTrace();
            logger.error("更新数据库异常：" + orderId + ":" + statusNo + ":"
                    + Utils.getShortMsg(statusText, RECHARGE_ORDER_OPERATED_DESC_MAX_LENGTH) + ":" + new Date());
        }

        return false;
    }

    public static class UpdateSucRechargeOrderStatusException extends Exception{
        private static final long serialVersionUID = 5434925941729479700L;

        public UpdateSucRechargeOrderStatusException(String message){
            super(message);
        }
    }

    /**
     * @Title: cmccRechargeAndRefund
     * @Description:调用crm充值接口，对于明确返回错误的，执行退费。不存在的订单，任务丢弃
     * @param orderId
     *            void
     * @param rechargeType
     */
    public void cmccRechargeAndRefund(String orderId, int rechargeType){
        logger.info("cmccRecharge:" + orderId);
        Record order = null;
        try{
            // 订单不存在的情况下，不抓异常，抓了也无法找到对应的order去记录异常情况
            order = RechargeOrder.dao.getOrderInfoByOrderId(orderId);
            if(order == null){
                logger.error("rechargeOrder does not exist： " + orderId);
                return;
            }
            // 过滤掉未支付的订单
            if(order.getInt(RechargeOrder.PAY_STATUS) != RechargeOrder.PAID){
                logger.error("order is not paid：" + orderId);
                return;
            }
            cmccRecharge(orderId, order, rechargeType);
        }
        catch(RechargeFailedException e){
            logger.error("RechargeFailedException:" + orderId);
            // 对于明确返回失败的订单（除重复充值返回错误以外），执行自动退费，并更新退费状态。
            // logger.info("orderId:" + orderId + ",to refund:" +
            // order.getDouble(CmccOrder.PAY_AMOUNT));
            CcbService.service.appRefund(orderId, String.valueOf(order.getDouble(RechargeOrder.PAY_AMOUNT)));
        }
        catch(Exception e){
            logger.error("recharge,exception,orderid:" + orderId);
            e.printStackTrace();
        }

    }

    /**
     * @Title: cmccRecharge
     * @Description:构造请求，请求并解析充值结果
     * @param orderId
     * @param order
     *            void
     * @param rechargeType
     */
    private void cmccRecharge(String orderId, Record order, int rechargeType) throws RechargeFailedException{
        String r = "";
        try{
            // 构造请求参数
            JSONObject param = buildJsonParam(order, orderId);

            // logger.info("recharge params : " + param.toString());

            // 发起办理业务请求
            r = rechargeByCmcc(param);
            // logger.info("r:" + r);

            // 解析请求结果，返回结果格式转换，若结构不对，则进入异常。
            parseResult(orderId, r, rechargeType);
        }
        catch(CheckParamException e){// 两种情况会抛这种异常：token异常、请求参数异常（未发起充值请求）
            // 此种异常，并未发起业务办理，需要自动退费
            // e.printStackTrace();
            logger.error("参数非法,未发起充值：" + e.getMessage() + ":" + orderId);
            // 此处异常msg不确定，截取长度128
            updateRechargeOrderStatus(orderId, "", RechargeOrder.ORDER_NOT_REQUEST, "参数非法,未发起充值：" + e.getMessage(), "");
            throw new RechargeFailedException("参数非法,未发起充值：" + e.getMessage());
        }
        catch(IOException e){
            // 办理网络异常，此种异常，已经发起业务办理，但没有得到返回，业务办理可能成功过了也可能失败了。
            logger.error("IOException:" + e.getMessage());
            updateRechargeOrderStatus(orderId, "", RechargeOrder.ORDER_REQUEST_NOT_BACK, "请求网络异常：" + e.getMessage(),
                                      "");
        }
        catch(RechargeFailedException e){
            // 有明确错误码的充值失败，抛出去进行退费。orderstatus在异常之前已经更新。这种异常，已经发起业务办理并得到反馈
            logger.error("RechargeFailedException:" + e.getMessage());
            updateRechargeOrderStatus(orderId, "", RechargeOrder.ORDER_FAILED, e.getMessage(), "");
            throw new RechargeFailedException(e.getMessage());
        }
        catch(ParseResultException e){
            e.printStackTrace();
            logger.error("充值结果解析异常：" + e.getMessage());
            // 这种异常，为解析失败异常，业务已经办理，但是返回的r可能出错，如JSON异常，如NullPointer异常.业务办理状态可能成功，也可能失败,与IOEXCEPTION类似
            updateRechargeOrderStatus(orderId, "", RechargeOrder.ORDER_REQUEST_RESULT_PARSE_ERROR,
                                      "充值结果解析异常：" + r + ":" + e.getMessage(), "");
        }
    }

    /**
     * @Title: rechargeByCmcc
     * @Description:发起充值请求，任务异常均返回IO异常到上一级统一处理.要么返回result，要么返回异常
     * @param param
     * @return String
     * @throws IOException
     */
    private String rechargeByCmcc(JSONObject param) throws IOException{
        logger.debug("rechargeByCmcc request url = " + REST_HOST_PREFIX + Constant.RestUrl.ESB_RECHARGE);
        logger.debug("request param:" + param.toString());
        // logger.info("rechargeByCmcc request url = " + REST_HOST_PREFIX +
        // Constant.RestUrl.ESB_RECHARGE);
        // logger.info("request param:" + param.toString());
        return HttpUtil.syncPostJsonBody(REST_HOST_PREFIX + Constant.RestUrl.ESB_RECHARGE, MediaType.JSON_UTF_8,
                                         param.toString(), 15000);
    }

    /**
     * @Title: parseResult
     * @Description:TODO
     * @param orderId
     * @param r
     *            void
     * @param rechargeType
     * @throws IOException
     */
    private void parseResult(String orderId, String r, int rechargeType)
            throws ParseResultException, RechargeFailedException, IOException{
        try{
            // r =
            // "{\"ROOT\":{\"RETURN_CODE\":0,\"RETURN_MSG\":\"OK\",\"USER_MSG\":\"OK\",\"DETAIL_MSG\":\"OK\",\"PROMPT_MSG\":\"OK\",\"OUT_DATA\":{\"PAY_ACCEPT\":\"1010102\",\"TOTAL_DATE\":20161130}}}";
            JSONObject result = new JSONObject(r);
            JSONObject root = result.getJSONObject(ROOT);
            String bossNo = "";
            String message = "";
            if(root.getInt(RETURN_CODE) == 0){
                bossNo = root.getJSONObject(OUT_DATA).getString(PAY_ACCEPT);
                message = root.getString(DETAIL_MSG);
                if(StrKit.notBlank(bossNo)){
                    String rechargeTypeDesc = getRechargeTypeDesc(rechargeType);
                    // 调用中转服务接口成功，更新订单状态为充值成功, order_status=5表示充值成功，不会抛异常出来
                    updateRechargeOrderStatus(orderId, bossNo, RechargeOrder.ORDER_SUC,
                                              rechargeTypeDesc + "：" + result.toString(), rechargeTypeDesc);
                    // 充值成功则保存到sales_promossion表，不会抛异常出来
                    save2SalesPromossion(orderId, bossNo, true, result.toString(), message);
                } else{
                    throw new RechargeFailedException("充值失败，bossNo为空：" + result.toString());
                }
            } else{
                if(root.getInt(RETURN_CODE) == 422000154){// 重复缴费错误码，1.6.1版本后的充值缴费接口重复充值会返回错误，故该错误不能去退费
                    logger.error(orderId + " ： 重复充值错误，不需要退费：" + result.toString());
                } else{
                    // 返回5000且有RETURN_FLAG字段，表示请求时rest或者rpc发生IOException，不能退费;
                    // 返回5000没有RETURN_FLAG字段，表示是BOSS端返回的有明确错误码的错误，可以退费
                    if(root.getInt(RETURN_CODE) == 5000 && root.has("RETURN_FLAG")
                            && "dzg".equals(root.getString("RETURN_FLAG"))){
                        logger.error(orderId + " ： rest或者rpc进行http请求时发生IOException，不需要退费：" + result.toString());
                        throw new IOException("充值失败，请求时rest或者rpc发生IOException：" + result.toString());
                    } else{
                        throw new RechargeFailedException("充值失败，有明确错误码：" + result.toString());
                    }
                }
            }
        }
        catch(RechargeFailedException e){
            // e.printStackTrace();
            logger.error("r返回错误码：" + e.getMessage());
            throw new RechargeFailedException(e.getMessage());
        }
        catch(IOException e){
            // e.printStackTrace();
            logger.error("r返回错误码：" + e.getMessage());
            throw new IOException(e.getMessage());
        }
        catch(Exception e){
            // e.printStackTrace();
            logger.error("r解析异常：" + e.getMessage());
            throw new ParseResultException("r异常:" + r + ":" + e.getMessage());
        }
    }

    /**
     * @Title: getRechargeTypeDesc
     * @Description:TODO
     * @param rechargeType
     * @return String
     */
    private String getRechargeTypeDesc(int rechargeType){
        String desc = "";
        switch(rechargeType){
            case 1:
                desc = "首次充值成功";
                break;
            case 2:
                desc = "二次充值成功";
                break;
            case 3:
                desc = "查询更新成功";
                break;
            case 4:
                desc = "手动查询充值成功";
                break;
            case 5:
                desc = "自动查询充值成功";
                break;
            default:
                break;
        }
        return desc;
    }

    /**
     * @Title: saveToSalesPromossion
     * @Description:充值缴费完成保存到移动业务清单表，相当于原来的回调函数
     * @param orderId
     *            void
     * @param cmccId
     *            TODO
     * @param isSuccess
     *            TODO
     * @param rechargeResult
     * @param message
     *            TODO
     * @paramresultObj
     */
    private boolean save2SalesPromossion(String orderId, String cmccId, boolean isSuccess, String rechargeResult,
            String message){
        try{
            return new SalePromotion().saveRechargeOrder(orderId, StrKit.notBlank(cmccId) ? cmccId : getCmccId(),
                                                         isSuccess, rechargeResult, message);
        }
        catch(Exception e){
            // e.printStackTrace();
            logger.error("更新移动业务清单数据库异常：" + orderId + ":" + (StrKit.notBlank(cmccId) ? cmccId : getCmccId()) + ":"
                    + Utils.getShortMsg(e.getMessage(), 128) + ":" + new Date());
        }

        return false;
    }

    /**
     * @Title: getCmccId
     * @Description:生成随机订单ID号
     * @return String
     */
    private String getCmccId(){
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String date = format.format(new Date());
        String orderId = "F" + date + (int)(Math.random() * 900) + 100;
        return orderId;
    }

    /**
     * @Title: buildJsonParam
     * @Description:判断参数合法性的同时，获取参数。要么抛异常，要么返回json数据
     * @param order
     * @param orderId
     * @return JSONObject
     */
    private JSONObject buildJsonParam(Record order, String orderId) throws CheckParamException{
        try{
            return buildJsonParams(order);
        }
        catch(Exception e){
            // e.printStackTrace();
            logger.error("参数异常：" + orderId + ":" + e.getMessage());
            throw new CheckParamException(e.getMessage());
        }
    }

    /**
     * @Title: buildJsonParams
     * @Description:TODO
     * @param rechargeOrder
     * @return JSONObject
     */
    private JSONObject buildJsonParams(Record rechargeOrder){
        JSONObject root = new JSONObject();
        JSONObject header = new JSONObject();
        JSONObject body = new JSONObject();
        // 1.7.0版本接口路径由/esop/rest/路径改为/rest/esb/路径，封装HEADER，改用ESB路径需要加HEADER部分
        // 充值缴费业务需要用用户手机号（被充值手机号）作路由，changed by zxl in 2017.9.1
        header.put("ROUTE_PHONE_NO", rechargeOrder.getStr(RechargeOrder.RECHARGE_PHONE));// 路由号码，被充值手机号
        root.put("HEADER", header);
        // 封装BODY
        body.put("LOGIN_NO", rechargeOrder.getStr(MemberOrg.EMP_CODE));// 当前用户的工号
        body.put("LOGIN_PASSWORD", "");
        body.put("GROUP_ID", rechargeOrder.getStr(Organization.NUMCODE));// 当前用户的渠道编码
        body.put("OP_CODE", "8000");
        body.put("CONTACT_NO", "");// 与文档s8000Cfm@接口文档.docx有区别：CONTRACT_NO，翁德志说可能是接口方手误，我们仍保持用CONTACT_NO
        String phone = rechargeOrder.getStr(RechargeOrder.RECHARGE_PHONE);// 充值手机号
        body.put("PHONE_NO", phone);
        Double money = rechargeOrder.getDouble(RechargeOrder.RECHARGE_MONEY).doubleValue() * 100;
        int moneyint = money.intValue();
        body.put("PAY_MONEY", String.valueOf(moneyint));
        body.put("PAY_PATH", "96");// 固定传96
        body.put("PAY_METHOD", "" + rechargeOrder.getInt(RechargeOrder.PAY_PLAT));// 这个字段与业支定义的有出入：业支：14-建行、15-支付宝、16-银联，大掌柜后台：14-支付宝、15-建行、16-银联，翁德志说只要传14-16都可以
        body.put("PAY_TYPE", "0");
        body.put("DELAY_RATE", "");
        body.put("REMONTH_RATE", "");
        body.put("BANK_CODE", "");
        body.put("CHECK_NO", "");
        // body.put("PAY_NOTE", moneyint + "|" + phone);
        body.put("PAY_NOTE", moneyint + "|" + phone + "|");// 新的充值缴费接口最后需要加上一个|符号
        // 外部流水，即支付接口返回的流水，若没有，则用订单号
        String orderId = rechargeOrder.getStr(RechargeOrder.ORDER_ID);
        String foreignSn = StrKit.notBlank(orderId) ? orderId.trim() : orderId;
        body.put("FOREIGN_SN", foreignSn);
        // body.put("FOREIGN_TIME", DateKit.toStr(new Date(),
        // "yyyyMMddHHmmss"));
        body.put("FOREIGN_TIME", DateKit.toStr(rechargeOrder.getDate(RechargeOrder.CREATE_TIME), "yyyyMMddHHmmss"));// 为了保证同一订单首次充值和二次充值传的缴费时间一致，故改传订单的创建时间
        // body.put("CTRL_FLAG", "");
        body.put("CTRL_FLAG", "0");// 新的crm充值缴费接口该字段要求传0
        body.put("PAY_RATE", "1");
        root.put("BODY", body);

        return root;
    }

    /**
     * 充值接口返回明确错误码时抛的异常
     */
    public static class RechargeFailedException extends Exception{

        private static final long serialVersionUID = 6780702742548362482L;

        public RechargeFailedException(String message){
            super(message);
        }
    }

    public static class ParseResultException extends Exception{

        private static final long serialVersionUID = -8569537874668851030L;

        public ParseResultException(String message){
            super(message);
        }
    }
}
